package com.efast.cafe.util;
import java.io.IOException;
import java.util.LinkedHashSet;
import java.util.Set;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.BeanDefinitionStoreException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.AbstractBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.ScannedGenericBeanDefinition;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.util.ClassUtils;

public class SpringUtils
{
  private static Logger logger = LoggerFactory.getLogger(SpringUtils.class);
  private  ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
  private  MetadataReaderFactory metadataReaderFactory = new CachingMetadataReaderFactory(resourcePatternResolver);
  ApplicationContext applicationContext;
  private static SpringUtils instance;

  public static SpringUtils getInstance(ApplicationContext applicationContext)
  {
    if (instance == null) {
      instance = new SpringUtils(applicationContext);
    }
    return instance;
  }
  SpringUtils(ApplicationContext applicationContext) {
    this.applicationContext = applicationContext;
  }
  private String resolveBasePackage(String basePackage) {
    return ClassUtils.convertClassNameToResourcePath(this.applicationContext.getEnvironment().resolveRequiredPlaceholders(basePackage));
  }

  public Set<BeanDefinition> findCandidateComponents(String basePackage, BeanDefinitionFilter sbdFlter)
  {
    Set<BeanDefinition> candidates = new LinkedHashSet<BeanDefinition>();
    try {
      String packageSearchPath = "classpath*:" + resolveBasePackage(basePackage) + "/" + "**/*.class";

      Resource[] resources = resourcePatternResolver.getResources(packageSearchPath);
      for (Resource resource : resources) {
        if (resource.isReadable())
          try {
            MetadataReader metadataReader = metadataReaderFactory.getMetadataReader(resource);
            ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
            sbd.setResource(resource);
            sbd.setSource(resource);
            if ((sbdFlter != null) && (sbdFlter.filter(sbd)))
              candidates.add(sbd);
          }
          catch (Throwable ex) {
            throw new BeanDefinitionStoreException("Failed to read candidate component class: " + resource, ex);
          }
      }
    }
    catch (IOException ex)
    {
      throw new BeanDefinitionStoreException("I/O failure during classpath scanning", ex);
    }
    return candidates;
  }

  public static Object createBean(Class<?> beanClass, String beanName, AbstractBeanFactory abstractBeanFactory, Object[] objParam) {
    DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory)abstractBeanFactory;
    if (!beanFactory.containsBean(beanName)) {
      BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.rootBeanDefinition(beanClass);
      AbstractBeanDefinition beanDefinition = beanDefinitionBuilder.getBeanDefinition();
      beanDefinition.setScope("prototype");
      beanFactory.registerBeanDefinition(beanName, beanDefinition);
    } else {
      logger.debug("beanName:" + beanName + "对应的bean已经存在！");
    }

    if (objParam != null) {
      return beanFactory.getBean(beanName, objParam);
    }
    return beanFactory.getBean(beanName);
  }
}